package com.xuzimian.demo.java.proxy.dynamic.performance

import com.xuzimian.demo.java.proxy.ISubject
import com.xuzimian.demo.java.proxy.RealSubject
import com.xuzimian.demo.java.proxy.dynamic.performance.handler.CglibHandler
import com.xuzimian.demo.java.proxy.dynamic.performance.handler.JavassistHandler
import com.xuzimian.demo.java.proxy.dynamic.performance.handler.JdkHandler
import javassist.ClassPool
import javassist.CtField
import javassist.CtNewConstructor
import javassist.CtNewMethod
import javassist.util.proxy.ProxyFactory
import javassist.util.proxy.ProxyObject
import net.sf.cglib.proxy.Enhancer
import java.lang.reflect.Proxy

object ProxyFactory {
    fun createJdkProxy(): ISubject {
        return Proxy.newProxyInstance(
                ClassLoader.getSystemClassLoader(), arrayOf<Class<*>>(ISubject::class.java),
                JdkHandler(object : ISubject {
                    override fun processBusiness(): String? {
                        return "Jdk"
                    }
                })) as ISubject
    }

    fun createCglibProxy(): ISubject {
        val enhancer = Enhancer()
        enhancer.setCallback(CglibHandler(object : ISubject {
            override fun processBusiness(): String? {
                return "Cglib"
            }
        }))
        enhancer.setInterfaces(arrayOf<Class<*>>(ISubject::class.java))
        return enhancer.create() as ISubject
    }

    fun createJavassistDynProxy(): ISubject? {
        val proxyFactory = ProxyFactory()
        proxyFactory.interfaces = arrayOf<Class<*>>(ISubject::class.java)
        val proxyClass = proxyFactory.createClass()
        var javassistProxy: ISubject? = null
        try {
            javassistProxy = proxyClass.newInstance() as ISubject
        } catch (e: InstantiationException) {
            e.printStackTrace()
        } catch (e: IllegalAccessException) {
            e.printStackTrace()
        }
        assert(javassistProxy != null)
        (javassistProxy as ProxyObject?)!!.handler = JavassistHandler(object : ISubject {
            override fun processBusiness(): String? {
                return "Javassist"
            }
        })
        return javassistProxy
    }

    fun createJavassistBytecodeDynamicProxy(): ISubject? {
        val classPool = ClassPool(true)
        val ctClass = classPool.makeClass(ISubject::class.java.name + "JavaassistBytecodeProxy")
        try {
            ctClass.addInterface(classPool[ISubject::class.java.name])
            ctClass.addConstructor(CtNewConstructor.defaultConstructor(ctClass))
            ctClass.addField(CtField.make(String.format("public %s real;", ISubject::class.java.name), ctClass))
            ctClass.addMethod(CtNewMethod.make(String.format("public String processBusiness() { if(real==null)real=new %s();return real.processBusiness(); }",
                    RealSubject::class.java.name), ctClass))
            return ctClass.toClass().newInstance() as ISubject
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }
}
